/* main.c */

#define _XOPEN_SOURCE

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ncurses.h>
#include <ctype.h>
#include <time.h>


#include "communication.h"
#include "protocol.h"
#include "ticket.h"

#define MAX_STRING 80 	/* longest allowed response		*/

#define MESSAGE_LINE 6	/* misc. messages on this line		*/
#define ERROR_LINE 22	/* line to use for errors		*/
#define Q_LINE 20	/* line for questions			*/
#define PROMPT_LINE 18	/* line for prompting on 		*/

#define COLOR_PAIR_1 1
#define COLOR_PAIR_2 2
#define COLOR_PAIR_3 3


static short color_bg;
static short color_fg;


void clear_all_screen(void);
void get_return(void);
int get_confirm(void);
int getchoice(char *greet, char *choices[]);
void draw_menu(char * options[],int highlight, int start_row, int start_col);
void get_string(char * string);
int user_login(void);
int tbs_exit(void);
void init_colors(void );

int book_ticket();
int list_book_history();

int check_user(const char *h, const char * l);
int query_tickets(ticket_t *tics, int max, const char *trainno, time_t start, time_t end);

int book(ticket_t * tic, int count);
	
char *main_menu[] = 
{
	"Book Ticket",
	"List Book History",
	"Quit",
	NULL,
};

int user_id;

int main(){
	//system varibles

	
	//ui stuff
	int choice;
	
	initscr();
	init_colors();
	communication_init();
	
	user_id = user_login();
	
	do{
		choice = getchoice("Select The Action You Want To Performed", main_menu);
		switch(choice){
		case 'b':
			book_ticket();
			break;
		case 'l':
			list_book_history();
			break;
		case 'q':
			tbs_exit();
			break;
		}
	}while(choice != 'q');
	endwin();
	exit(EXIT_SUCCESS);
}

int tbs_exit(){
	endwin();
	exit(EXIT_SUCCESS);
	return 0;
}

int user_login(void){
	int id = -1;
	int i = 0;
	int row = 5;
	int col = COLS/2 - 15;
	char user_name[MAX_STRING];
	char user_pwd[MAX_STRING];
	for(i = 0;i<3;i++){
		clear_all_screen();
		mvprintw(row, col + 8, "User Login");
		mvprintw( row + 2, col, "User Name: ");
		get_string(user_name);
		if(strcmp(user_name, "exit") == 0){
			tbs_exit();
		}
		mvprintw( row + 4, col, "Password : ");
		noecho();
		get_string(user_pwd);
		echo();
		if((id = check_user(user_name, user_pwd)) <0){
			if(i == 2){
				tbs_exit();
			}else{
				
				if(id == INVALID_USR){
					clear_all_screen();
					attron(COLOR_PAIR(1));
					mvprintw( row+ 5, col, "Invalid User Name");
					attroff(COLOR_PAIR(1));
					refresh();
					sleep(2);
				}else if (id == INVALID_PWD){
					clear_all_screen();
					attron(COLOR_PAIR(1));
					mvprintw( row+ 5, col, "Invalid Password");
					attroff(COLOR_PAIR(1));
					refresh();
					sleep(2);
				}else{
					clear_all_screen();
					attron(COLOR_PAIR(1));
					mvprintw( row+ 5, col, "Server Busy");
					attroff(COLOR_PAIR(1));
					refresh();
					sleep(2);
				}
			}
		}else{
			clear_all_screen();
			attron(COLOR_PAIR(3));
			mvprintw( row+ 5, col, "Login Succeed");
			attroff(COLOR_PAIR(3));
			refresh();
			sleep(2);
			return id;
		}
	}
	return 0;
}

void init_colors(){
	if(has_colors()){
		if(start_color() == OK ){
			//init color pairs
			pair_content(0, &color_fg, &color_bg);

			init_pair(COLOR_PAIR_1, COLOR_RED, color_bg );
			init_pair(COLOR_PAIR_2, COLOR_BLUE, color_bg );
			init_pair(COLOR_PAIR_3, COLOR_GREEN, color_bg );
		}
	}
}


int getchoice(char *greet, char *choices[]){
	static int selected_row = 0;
	int max_row = 0;
	int start_screenrow = MESSAGE_LINE,
	    start_screencol = 10;
	char **option;
	int selected;
	int key = 0;
	option = choices;
	while(*option){
		max_row++;
		option++;
	}
	/* protect against menu getting shorter when CD deleted */
	if(selected_row >= max_row)
		selected_row = 0;
	clear_all_screen();
	mvprintw(start_screenrow - 2, start_screencol, greet);
	keypad(stdscr, TRUE);
	cbreak();
	noecho();
	key = 0;
	while(key != 'q' && key != KEY_ENTER && key != '\n'){
		if( key == KEY_UP){
			if(selected_row == 0)
				selected_row = max_row -1;
			else
				selected_row --;
		}
		if( key == KEY_DOWN){
			if(selected_row == (max_row - 1))
				selected_row = 0;
			else
				selected_row ++;
		}
		selected = *choices[selected_row];
		draw_menu(choices, selected_row, start_screenrow, start_screencol);
		key = getch();
	}
	keypad(stdscr, FALSE);
	nocbreak();
	echo();
	if(key == 'q'){
		selected = 'q';
	}
	return tolower(selected);
}

void draw_menu(char *options[],int current_highlight, int start_row, int start_col){
	int current_row = 0;
	char **option_ptr;
	char * txt_ptr;
	option_ptr = options;
	while(*option_ptr){
		if(current_row == current_highlight) attron(COLOR_PAIR(COLOR_PAIR_2) | A_BOLD);
		txt_ptr = options[current_row];
		mvprintw(start_row + current_row, start_col, "%s", txt_ptr);
		if(current_row == current_highlight) attroff(COLOR_PAIR(COLOR_PAIR_2) | A_BOLD);
		current_row++;
		option_ptr ++;
	}
	mvprintw(start_row + current_row +3, start_col, "Move highlight then press Enter");
	refresh();
}

void clear_all_screen(){
	char *title = "Live Ticket Booking System";
	int len = strlen(title);
	clear();
	attron(A_BOLD);
	mvprintw(2,(COLS -len)/2 ,"%s",title);
	attroff(A_BOLD);
	refresh();
}




void get_return(){
	int ch;
	mvprintw(23,0,"%s", " Press return ");
	refresh();
	while((ch = getchar()) != '\n' && ch != EOF);
}



void get_string(char * string){
	int len;
	wgetnstr(stdscr, string, MAX_STRING);
	len = strlen(string);
	if(len >0 && string[len - 1] == '\n')
		string[len -1] = '\0';
}

int get_confirm(){
	int confirmed = 0;
	char first_char;
	mvprintw(Q_LINE, 5, "Are you sure? ");
	clrtoeol();
	refresh();
	cbreak();
	first_char= getch();
	if(first_char == 'Y' || first_char == 'y'){
		confirmed = 1;
	}
	nocbreak();
	if(!confirmed){
		mvprintw(Q_LINE, 1, "\tCancelled");
		clrtoeol();
		refresh();
		sleep(1);
	}
	return confirmed;
}

int check_user(const char *user_name, const char * user_pwd){
	protocol_t out_msg;
	protocol_t in_msg;
	char msg_buf[512];
	empty_msg(&out_msg);
	empty_msg(&in_msg);	
	out_msg.act_type = ASK_LOGIN;

	strcpy(out_msg.usr_name, user_name);
	strcpy(out_msg.usr_pwd, user_pwd);
	
	if(format_msg(&out_msg, msg_buf) != 0){
		perror("Cannot format message!");
		tbs_exit();
	}
	send_msg(msg_buf);
	recv_msg(msg_buf, 512);
	
	if(parse_msg(&in_msg, msg_buf) != 0){
		perror("\nCannot parse message!");
		tbs_exit();
	}
	
	return in_msg.usr_id;
}


int book_ticket(){
	int row = 4;
	int col = COLS/2 - 15;
	char trainno[MAX_STRING];
	char time_s[MAX_STRING];
	char time_e[MAX_STRING];
	char time1[MAX_STRING];

	
	time_t time_start;
	time_t time_end;
	struct tm tim_s;
	struct tm tim_e;

	const int MAX_TICKET = 10;
	ticket_t tickets[MAX_TICKET];
	int ticket_num = 0;
	int selected_id;
	int selected_num;
	
	clear_all_screen();
	mvprintw(row, col + 8, "Book Ticket");
	mvprintw( row + 2, col, "Train No: ");
	get_string(trainno);
	if(strcmp(trainno, "e") == 0){
		return 0;
	}
	mvprintw( row + 4, col, "Time Range ");
	
	while(1){
		char *ptr;
		move(row+6,col);
		clrtoeol();
		mvprintw(row + 6, col, "From: ");
		get_string(time_s);
		ptr = strptime(time_s,"%Y-%m-%d %H:%M:%S",&tim_s);
		time_start = mktime(&tim_s);
		if(ptr != NULL && *ptr == '\0')
			break;
	}
	while(1){
		char *ptr;
		move(row+7,col);
		clrtoeol();
		mvprintw(row + 7, col, "To  : ");
		get_string(time_e);
		ptr = strptime(time_e,"%Y-%m-%d %H:%M:%S",&tim_e);
		time_end = mktime(&tim_e);
		if(ptr != NULL && *ptr == '\0')
			break;
	}
	
	ticket_num = query_tickets(tickets, MAX_TICKET, trainno, time_start, time_end);
	
	clear_all_screen();
	if(ticket_num <= 0){
		mvprintw(row, col + 8, "Book Ticket");
		mvprintw( row + 2, col, "No Matched Ticket");
		mvprintw( row + 4, col, "Press Enter To Get Return");
		get_return();
		return 0;
	}else{
		int start_row = row + 2;
		int i ;
		for(i = 0;i < ticket_num; i++){
			gmtime_r(&(tickets[i].time), &tim_s);
			strftime(time1, MAX_STRING,"%Y-%m-%d %H:%M:%S",&tim_s);
			mvprintw(start_row+i, 4, "%d\t%s\t%s\t%d\t%s",
				i, 
				tickets[i].trainno,
				time1,
				tickets[i].num,
				tickets[i].description);
		}
		refresh();
	}
	
	while(TRUE){
		mvprintw(Q_LINE, 5, "Enter The Ticket ID: ");
		get_string(trainno);
		if((selected_id = atoi(trainno))<=ticket_num && selected_id >=0){
			if(tickets[selected_id].num != 0)
				break;
		}
	}
	while(TRUE){
		clrtoeol();
		mvprintw(Q_LINE, 5, "Enter The Ticket Count: ");
		get_string(trainno);
		if((selected_num = atoi(trainno))<=tickets[selected_id].num && selected_num >0)
			break;
	}
	
	if(get_confirm()){
		book(&(tickets[selected_id]), selected_num);
	}
	return 0;
}


int query_tickets(ticket_t *tics, int max, const char *trainno, time_t time_start, time_t time_end){
	int i;
	protocol_t in_msg;
	protocol_t out_msg;
	char msg_buf[512];
	
	empty_msg(&in_msg);
	empty_msg(&out_msg);
	
	out_msg.act_type = ASK_QUERY_TICKET;

	out_msg.usr_id = user_id;
	out_msg.time_start = time_start;
	out_msg.time_end = time_end;
	strcpy(out_msg.train_no, trainno);
	
	if(format_msg(&out_msg, msg_buf) != 0){
		perror("Cannot format message!");
		tbs_exit();
	}
	// send query
	send_msg(msg_buf);
	
	// recieve replies
	for(i = 0;;i++){
		recv_msg(msg_buf, 512);
	//	mvprintw(23, 0, "recieved: %s", msg_buf);
	//	refresh();
		if(parse_msg(&in_msg, msg_buf) != 0){
			perror("\nCannot parse message!");
			tbs_exit();
		}
		if(in_msg.ticket_id >= 0){
			if(i>= max)continue;
			tics[i].id = in_msg.ticket_id;
			tics[i].time = in_msg.time_start;
			tics[i].num = in_msg.ticket_count;
			strcpy(tics[i].description,in_msg.other);
			strcpy(tics[i].trainno, in_msg.train_no);
		}else{
			return i;
		}
	}
	return 0;
}

int list_book_history(){
	int row = 5;
	protocol_t in_msg;
	protocol_t out_msg;
	char msg_buf[512];
	char order_time[256];
	char time[256];
	struct tm tim1;
	struct tm tim2;
	
	empty_msg(&in_msg);
	empty_msg(&out_msg);
	
	out_msg.act_type = ASK_QUERY_BOOKING;

	out_msg.usr_id = user_id;
	
	if(format_msg(&out_msg, msg_buf) != 0){
		perror("Cannot format message!");
		tbs_exit();
	}
	// send query
	send_msg(msg_buf);
	// recieve replies
	
	clear_all_screen();
	while(1){
	
		recv_msg(msg_buf, 512);
	//	mvprintw(23, 0, "recieved: %s", msg_buf);
	//	refresh();
		if(parse_msg(&in_msg, msg_buf) != 0){
			perror("\nCannot parse message!");
			tbs_exit();
		}
		if(in_msg.order_no >= 0){
			if(row >= LINES -2)continue;
		
			gmtime_r(&(in_msg.time_start), &tim1);
			strftime(order_time, MAX_STRING,"%Y-%m-%d %H:%M:%S",&tim1);
	
			gmtime_r(&(in_msg.time_end), &tim2);
			strftime(time, MAX_STRING,"%Y-%m-%d %H:%M:%S",&tim2);
			
			mvprintw(++row, 0 , "    %d\t%s\t%s\t%s\t%d\t%s",
					in_msg.order_no,
					order_time,
					in_msg.train_no,
					time,
					in_msg.ticket_count,
					in_msg.other);

		}else{
			break;
		}
	}
	if(row == 4){
		mvprintw(5, 20,"You haven't book any ticket!");
		refresh();
	}else{
		move(++row, 0);
		clrtoeol();
	}
	get_return();
	return 0;
}

int book(ticket_t * tic, int count){

	protocol_t in_msg;
	protocol_t out_msg;
	char msg_buf[512];
	
	empty_msg(&in_msg);
	empty_msg(&out_msg);
	
	out_msg.act_type = ASK_BOOK;

	out_msg.usr_id = user_id;
	out_msg.ticket_id = tic->id;
	out_msg.ticket_count = count;
	
	if(format_msg(&out_msg, msg_buf) != 0){
		perror("Cannot format message!");
		tbs_exit();
	}
	// send query
	send_msg(msg_buf);
	
	recv_msg(msg_buf, 512);
	//	mvprintw(23, 0, "recieved: %s", msg_buf);
	//	refresh();
	if(parse_msg(&in_msg, msg_buf) != 0){
		perror("\nCannot parse message!");
		tbs_exit();
	}

	if(in_msg.ticket_id >= 0){
		clear_all_screen();
		mvprintw(8, 30,"Book Succeeded!");
		refresh();
		get_return();
	}else{
		clear_all_screen();
		mvprintw(8, 10, "Sorry, There are only %d tickets left.", in_msg.ticket_count);
		refresh();
		get_return();
	}
	return 0;
}

